#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "lex_an.h"
#include "pamet.h"
#include "ce_chars.h"

//#include <conio.h>

inline Latomicky *malopameti(Latomicky *ptr){malopameti((void*)ptr);return ptr;}
inline Lseznam *malopameti(Lseznam *ptr){malopameti((void*)ptr);return ptr;}

int Lmezery=1;

Lanstav::Lanstav(FILE *fil){
  f=fil;
  tag=-1;
  zav=0;
  pzav=0;
  stav=0;
  rad=1;
  sl=0;
  zn=' ';
  cti_zn();
}

int Lanstav::cti_zn(void){
  if(zn=='\n'){
    rad++;
    sl=1;
  }else sl++;
  zn=getc(f);
  if(Lmezery) while(isspace(zn)){
    if(zn=='\n'||zn=='\r'){
      int pz=getc(f);
      if((pz!='\n'&&pz!='\r')||pz==zn) ungetc(pz,f);
      zn='\n';
    }
    if(zn=='\n'){
      rad++;
      sl=1;
    }else sl++;
    zn=getc(f);
  }
  if(zn=='\n'||zn=='\r'){
    int pz=getc(f);
    if((pz!='\n'&&pz!='\r')||pz==zn) ungetc(pz,f);
    zn='\n';
  }
  return zn;
}

void Lanstav::cti_id(string &s,int &p){
  s[p++]=toupper(zn);
  cti_zn();
  while(isalnum_ce(zn)||zn=='_'){
    if(p<255) s[p++]=toupper_ce(zn);
    cti_zn();
  }
  s[p]=0;
}

void Lanstav::cti_jmzb(string &s,int &p){
  if(p==1&&zn=='*')
    if(s[0]>='A'&&s[0]<='F') tag=_Latomovapromenna;
    else if(s[0]>='I'&&s[0]<='N') tag=_Lstromovapromenna;
    else if(s[0]>='U'&&s[0]<='Z') tag=_Lseznamovapromenna;
    else tag=_Latom;
  else tag=_Latom;
  if(tag>_Latom){
    cti_id(s,p);
    if(p==3&&s[2]=='0') s[--p]=0;
  }
}

void Lanstav::cti_dvojty(string &s,int &p,int znak,int ztag){
  s[p++]=znak;
  cti_zn();
  if(zn==znak){
    s[p++]=zn;
    tag=ztag;
    cti_zn();
    s[p]=0;
  }else{
    if(isalnum(zn)||zn=='_') cti_id(s,p);
    else s[p]=0;
    tag=_Latom;
  }
}

void Lanstav::cti_uvnitr(string &s,int &p){
  if(tag==_Ldekompozice){
    cti_id(s,p);
    cti_jmzb(s,p);
  }else if(zn==','&&tag!=_Lcarka&&tag!=_Llevazavorka){
    s[p++]=',';
    tag=_Lcarka;
    cti_zn();
    s[p]=0;
  }else if(zn=='('&&tag!=_Lcarka&&tag!=_Llevazavorka){
    zav++;
    s[p++]='(';
    tag=_Llevazavorka;
    cti_zn();
    s[p]=0;
  }else if(zn==')'&&tag!=_Lcarka&&tag!=_Llevazavorka){
    zav--;
    s[p++]=')';
    tag=_Lpravazavorka;
    cti_zn();
    s[p]=0;
  }else if(zn=='$'){
    cti_dvojty(s,p,'$',_Ldekompozice);
  }else{
    cti_id(s,p);
    if(zn=='-'&&(strcmp((char*)s,"-")==0||strcmp((char*)s,"-NUL")==0)){
      s[p++]='-';
      tag=(p==2)?_Lkopie:_Lnul;
      cti_zn();
      s[p]=0;
    }else cti_jmzb(s,p);
  }
}

Lseznam *Lanstav::nacti_slovo_programu(void){
  if(zn==EOF) return NULL;
  string s;
  int p=0,t=-1;
  //preskoceni bilych znaku a komentaru
  while((isspace(zn)||zn=='*')&&t<0){
    if(zn=='*'){ //komentar nebo atom?
      cti_zn();
      if(stav==0&&zn=='*'){ //je to komentar
        while(zn!='.'&&zn!=EOF) cti_zn();
        cti_zn();
      }else{       //je to atom
        s[p++]='*';
        if(isalnum(zn)||zn=='_') cti_id(s,p);
        else s[p]=0;
        t=_Latom;
        if(!stav) stav=1;
      }
    }else cti_zn();
  }
  if(zn==EOF) return NULL;
  unsigned long pr=rad,ps=sl;
  //vlastni analyza
  if(t==0){
    tag=t;
  }else switch(stav){
    case 0://mimo pravidlo
      cti_id(s,p);
      if(zn=='-'&&(strcmp(s,"-REQ")==0||strcmp(s,"-DET")==0||strcmp(s,"-INV")==0)){
        s[p++]='-';
        tag=(s[1]=='R')?_Lreq:((s[1]=='D')?_Ldet:_Linv);
        cti_zn();
        s[p]=0;
      }else if(zn=='-'){
        s[p++]='-';
        tag=_Lkopie;
        cti_zn();
        s[p]=0;
        stav=1;
      }else{
        cti_jmzb(s,p);
        stav=1;
      }
      break;
    case 1://leva strana pravidla
      if(zav){
        cti_uvnitr(s,p);
      }else{
        if(zn=='+'&&tag!=_Lplus){
          s[p++]='+';
          tag=_Lplus;
          cti_zn();
          s[p]=0;
        }else if(zn=='('&&tag!=_Lplus){
          zav++;
          s[p++]='(';
          tag=_Llevazavorka;
          cti_zn();
          s[p]=0;
        }else if(zn=='='&&tag!=_Lplus){
          cti_dvojty(s,p,'=',_Lprepis);
          if(tag==_Lprepis) stav=2;
        }else if(zn=='-'){
          cti_dvojty(s,p,'-',_Lkopie);
        }else{
          cti_id(s,p);
          cti_jmzb(s,p);
        }
      }
      break;
    case 2://prava strana pravidla
      if(zav){
        cti_uvnitr(s,p);
      }else{
        if(zn=='+'&&tag!=_Lplus){
          s[p++]='+';
          tag=_Lplus;
          cti_zn();
          s[p]=0;
        }else if(zn=='('&&tag!=_Lplus){
          zav++;
          s[p++]='(';
          tag=_Llevazavorka;
          cti_zn();
          s[p]=0;
        }else if(zn=='/'&&tag!=_Lplus&&tag!=_Lprepis){
          s[p++]='/';
          tag=_Lpodminka;
          cti_zn();
          s[p]=0;
          stav=3;
        }else if(zn=='.'&&tag!=_Lplus&&tag!=_Lprepis){
          s[p++]='.';
          tag=_Ltecka;
          cti_zn();
          s[p]=0;
          stav=0;
        }else if(zn=='-'){
          cti_dvojty(s,p,'-',_Lkopie);
        }else{
          cti_id(s,p);
          cti_jmzb(s,p);
        }
      }
      break;
    case 3://podminka
      if(zav){
        cti_uvnitr(s,p);
      }else{
        if(tag==_Ldekompozice){
          cti_id(s,p);
          cti_jmzb(s,p);
        }else if(zn=='-'){
          cti_id(s,p);
          if(zn=='-'){
            if((tag==_Lcarka||(tag>=_Lrovnost&&tag<=_Lhors))&&strcmp(s,"-NUL")==0){
              s[p++]='-';
              tag=_Lnul;
              cti_zn();
              s[p]=0;
            }else
            if((tag==_Lpodminka||tag==_Llevapodmzavorka||(tag>=_Lou&&tag<=_Lnon))&&(strcmp(s,"-")==0||strcmp(s,"-NON")==0||strcmp(s,"-NUL")==0)){
              s[p++]='-';
              tag=(p==2)?_Lkopie:((s[2]=='O')?_Lnon:_Lnul);
              cti_zn();
              s[p]=0;
            }else
            if((tag<=_Lseznamovapromenna||tag==_Lpravazavorka||tag==_Lnul||tag==_Lpravapodmzavorka||tag==_Lkopie)&&(strcmp(s,"-ET")==0||strcmp(s,"-OU")==0)){
              s[p++]='-';
              tag=(s[1]=='E')?_Let:_Lou;
              cti_zn();
              s[p]=0;
            }else
            if((tag<=_Lseznamovapromenna||tag==_Lpravazavorka||tag==_Lnul)&&(strcmp(s,"-DANS")==0||strcmp(s,"-HORS")==0)){
              s[p++]='-';
              tag=(s[1]=='D')?_Ldans:_Lhors;
              cti_zn();
              s[p]=0;
            }else cti_jmzb(s,p);
          }else cti_jmzb(s,p);
        }else if(zn=='('){
          s[p++]='(';
          cti_zn();
          if(tag<_Lseznamovapromenna){
            zav++;
            tag=_Llevazavorka;
            s[p]=0;
          }else
          if(zn=='.'&&(tag==_Lpodminka||tag==_Llevapodmzavorka||(tag>=_Lou&&tag<=_Lnon))){
            pzav++;
            s[p++]='.';
            tag=_Llevapodmzavorka;
            cti_zn();
            s[p]=0;
          }else{
            if(isalnum(zn)||zn=='_') cti_id(s,p);
            else s[p]=0;
            tag=_Latom;
          }
        }else
        if(zn=='.'&&(tag<=_Lseznamovapromenna||tag==_Lpravazavorka||tag==_Lnul||tag==_Lpravapodmzavorka||tag==_Lkopie)){
          s[p++]='.';
          cti_zn();
          if(pzav){
            if(zn==')'){
              pzav--;
              s[p++]=')';
              tag=_Lpravapodmzavorka;
              cti_zn();
              s[p]=0;
            }else{
              if(isalnum(zn)||zn=='_') cti_id(s,p);
              else s[p]=0;
              tag=_Latom;
            }
          }else{
            tag=_Ltecka;
            s[p]=0;
            stav=0;
          }
        }else if(zn==','&&(tag<=_Lseznamovapromenna||tag==_Lpravazavorka||tag==_Lnul)){
          s[p++]=',';
          tag=_Lcarka;
          cti_zn();
          s[p]=0;
        }else if(zn=='='&&(tag<=_Lseznamovapromenna||tag==_Lpravazavorka||tag==_Lnul)){
          s[p++]='=';
          tag=_Lrovnost;
          cti_zn();
          s[p]=0;
        }else if(zn=='"'&&(tag<=_Lseznamovapromenna||tag==_Lpravazavorka||tag==_Lnul)){
          s[p++]='"';
          tag=_Lnerovnost;
          cti_zn();
          s[p]=0;
        }else
        if(zn=='$'&&(tag==_Lcarka||tag==_Lpodminka||tag==_Llevapodmzavorka||(tag>=_Lrovnost&&tag<=_Lhors)||(tag>=_Lou&&tag<=_Lnon))){
          cti_dvojty(s,p,'$',_Ldekompozice);
        }else{
          cti_id(s,p);
          cti_jmzb(s,p);
        }
      }
      break;
  }
  switch(tag){
    int i;
    case _Latom:
      return malopameti(new Ltext(malopameti(new Latom(s)),pr,ps));
    case _Latomovapromenna:
    case _Lstromovapromenna:
    case _Lseznamovapromenna:
      for(i=1;s[i];i++) s[i]=s[i+1];
      switch(tag){
        case _Latomovapromenna:
          return malopameti(new Ltext(malopameti(new Latomovapromenna(s)),pr,ps));
        case _Lstromovapromenna:
          return malopameti(new Ltext(malopameti(new Lstromovapromenna(s)),pr,ps));
        case _Lseznamovapromenna:
          return malopameti(new Ltext(malopameti(new Lseznamovapromenna(s)),pr,ps));
      }
    default:
      return malopameti(new Lkonstanta(tag,pr,ps));
  }
}

Lseznam *Lanstav::nacti_slovo_vstupu(void){
  if(zn==EOF) return NULL;
  string s;
  int p=0;
  //preskoceni bilych znaku
  while(isspace(zn)) cti_zn();
  if(zn==EOF) return NULL;
  unsigned long pr=rad,ps=sl;
  //vlastni analyza
  switch(stav){
    case 0://mimo cestu
      if(zn=='/'){
        s[p++]='/';
        tag=_Llomitko;
        cti_zn();
        s[p]=0;
      }else{
        cti_id(s,p);
        if(s[0]=='-'&&zn=='-'&&p>1){
          int i=1;
          unsigned long n=0,mez=_Lmaxvrchol/10;
          for(i=1;isdigit(s[i]);i++){
            if(n>=mez) n=_Lmaxvrchol;
            else n=n*10+s[i]-'0';
          }
          if(p==i){
            s[p++]='-';
            tag=_Lmaxtag+n;
            cti_zn();
            s[p]=0;
          }else cti_jmzb(s,p);
        }else cti_jmzb(s,p);
        stav=1;
      }
      break;
    case 1://v ceste
      if(zav){
        cti_uvnitr(s,p);
      }else{
        if(zn=='+'&&tag!=_Lplus&&tag<_Lmaxtag){
          s[p++]='+';
          tag=_Lplus;
          cti_zn();
          s[p]=0;
        }else if(zn=='('&&tag!=_Lplus&&tag<_Lmaxtag){
          zav++;
          s[p++]='(';
          tag=_Llevazavorka;
          cti_zn();
          s[p]=0;
        }else{
          cti_id(s,p);
          if(s[0]=='-'&&zn=='-'&&tag!=_Lplus&&tag<_Lmaxtag&&p>1){
            int i=1;
            unsigned long n=0,mez=_Lmaxvrchol/10;
            for(i=1;isdigit(s[i]);i++){
              if(n>=mez) n=_Lmaxvrchol;
              else n=n*10+s[i]-'0';
            }
            if(p==i){
              s[p++]='-';
              tag=_Lmaxtag+n;
              cti_zn();
              s[p]=0;
              stav=0;
            }else cti_jmzb(s,p);
          }else cti_jmzb(s,p);
        }
      }
      break;
  }
  switch(tag){
    int i;
    case _Latom:
      return malopameti(new Ltext(malopameti(new Latom(s)),pr,ps));
    case _Latomovapromenna:
    case _Lstromovapromenna:
    case _Lseznamovapromenna:
      for(i=1;s[i];i++) s[i]=s[i+1];
      switch(tag){
        case _Latomovapromenna:
          return malopameti(new Ltext(malopameti(new Latomovapromenna(s)),pr,ps));
        case _Lstromovapromenna:
          return malopameti(new Ltext(malopameti(new Lstromovapromenna(s)),pr,ps));
        case _Lseznamovapromenna:
          return malopameti(new Ltext(malopameti(new Lseznamovapromenna(s)),pr,ps));
      }
    default:
      if(tag<_Lmaxtag) return malopameti(new Lkonstanta(tag,pr,ps));
      else return malopameti(new Lvrchol(tag-_Lmaxtag,pr,ps));
  }
}

void smaz_Lseznam(Lseznam *l){
  while(l){
    Lseznam *p=l;
    l=l->dal();
    delete p;
  }
}

void test_Lseznamu(Lseznam* v)
{
  Lseznam* p=v;
  while(p!=NULL){
    if(p->tag()<=_Lseznamovapromenna){
      printf("===>%s<===\r\n",p->obsah());
    }else if(p->tag()<_Lmaxtag){
      printf("===>%lu<===\r\n",p->tag());
    }else{
      printf("===>-%lu-<===\r\n",p->tag()-_Lmaxtag);
    }
    p=p->dal();
//    getch();
  }
  while(v!=NULL){
    p=v;
    v=v->dal();
    delete p;
  }
}

Lseznam* spoj_Lseznamy(Lseznam* ls,Lseznam* ls2)
{
  if(!ls) return ls2;
  else {
    Lseznam* l=ls;
    while(l->dal()) l=l->dal();
    Lseznam* lk=malopameti(new Lkonstanta(_Lreq,ls2->radek(),ls2->sloupec()));
    l->pridej(lk);
    lk->pridej(ls2);
    return ls;
  }
}